import java.awt.Point;
import java.util.*;

public class SnakeClient {

	int[] LEFT = new int[2];
	int[] RIGHT = new int[2];
	int[] UP = new int[2];
	int[] DOWN = new int[2];
	int[] NONE = new int[2];
	
	int counter = 0;
	Random generator;
	int[][] food = null;
	int foodSize = 10;
	int sceneX = 10;
	int sceneY = 10;
	
	public SnakeClient()
	{
		LEFT[0] = -1;
		LEFT[1] = 0;
		
		RIGHT[0] = 1;
		RIGHT[1] = 0;
		
		UP[0] = 0;
		UP[1] = 1;
		
		DOWN[0] = 0;
		DOWN[1] = -1;
		
		NONE[0] = 0;
		NONE[1] = 0;
		
		generator = new Random(System.currentTimeMillis()/1000);
		food = new int[foodSize][2];
	}
	
	public int[] GetFood(List<int[]> food, List<int[]> snake)
	{
		if (food.size() < foodSize)
		{
			if (counter %1 == 0)
			{
				int[] nf = null;
				do
				{
					nf = new int[2];
					nf[0] = generator.nextInt(sceneX);
					nf[1] = generator.nextInt(sceneY);
				}
				while(IsPointOnTheList(snake, nf) || IsPointOnTheList(food,nf));
				
				food.add(nf);
				return nf;
			}
		}
		
		counter++;
		return null;
	}
	
	public int[][] GetSnake()
	{
		int[][] snk = new int[3][2];
		snk[0][0] = 0;
		snk[0][1] = 2;		
		snk[1][0] = 0;
		snk[1][1] = 1;		
		snk[2][0] = 0;
		snk[2][1] = 0;		
		return snk;
	}

	public int[] GetSceneDim()
	{
		int[] sd = new int[2];
		sd[0] = sceneX;
		sd[1] = sceneX;
		return sd;
	}
	
	public boolean IsPointCollidingWithTail(List<int[]> snake, int[] p)
	{
		for (int ii = 1; ii < snake.size(); ++ii)
		{
			if (snake.get(ii)[0] == p[0] && snake.get(ii)[1] == p[1])
			{
				return true;
			}
		}
		
		return false;
	}
	
	public boolean IsPointOnTheList(List<int[]> list, int[] p)
	{
		for (int ii = 0; ii < list.size(); ++ii)
		{
			if (list.get(ii)[0] == p[0] && list.get(ii)[1] == p[1])
			{
				return true;
			}
		}
		
		return false;		
	}
	
	public boolean IsPointCollidingWithWall(int maxX, int maxY, int[] p)
	{
		if (p[0] >= maxX || p[0] < 0)
			return true;
		if (p[1] >= maxY || p[1] < 0)
			return true;
			
		return false;
	}
	
	private boolean TryLenghtenSnakeInGivenDirection(List<int[]> snake, int[] sceneDim, int[] direction)
	{
		int[] np = new int[2];
		np[0] = snake.get(snake.size()-1)[0] + direction[0];
		np[1] = snake.get(snake.size()-1)[1] + direction[1];
		if (!IsPointCollidingWithTail(snake, np) && !IsPointCollidingWithWall(sceneDim[0], sceneDim[1], np))
		{
			snake.add(np);
			return true;
		}	
		
		return false;
	}
	
	public void LenghtenSnake(List<int[]> snake, int[] sceneDim) throws Exception
	{
		if (TryLenghtenSnakeInGivenDirection(snake, sceneDim, LEFT))
		{
			return;
		}
		if (TryLenghtenSnakeInGivenDirection(snake, sceneDim, RIGHT))
		{
			return;
		}
		if (TryLenghtenSnakeInGivenDirection(snake, sceneDim, UP))
		{
			return;
		}
		if (TryLenghtenSnakeInGivenDirection(snake, sceneDim, DOWN))
		{
			return;
		}
			
		throw new Exception("failed to lenghten snake");
	}
	
	public boolean IsHeadColliding(int sceneX, int sceneY, List<int[]> snake)
	{
		return IsPointCollidingWithTail(snake, snake.get(0)) || IsPointCollidingWithWall(sceneX, sceneY, snake.get(0));
	}
	
	public int[] GetXDir(int[] vec)
	{
		if (vec[0] > 0)
			return RIGHT;
		else
		if (vec[0] == 0)
			return NONE;
			
		return LEFT;
	}
	
	public int[] GetYDir(int[] vec)
	{
		if (vec[1] > 0)
			return UP;
		else
		if (vec[1] == 0)
			return NONE;
		
		return DOWN;
	}	
	
	public void SimulateByOne(int[] direction, List<int[]> snake)
	{
		if (direction.equals(NONE))
			return;
	
		int[] nh = new int[2];
		nh[0] = snake.get(0)[0] + direction[0];
		nh[1] = snake.get(0)[1] + direction[1];
		snake.remove(snake.size() - 1);
		snake.add(0, nh);
	}
	
	public boolean Simulate(int[] direction, int units, List snake, int sceneX, int sceneY)
	{
		for (; units > 0; --units)
		{
			SimulateByOne(direction, snake);
			if (IsHeadColliding(sceneX, sceneY, snake))
			{
				return false;
			}
		}
		
		return true;
	}
	
	public boolean SimulateXY(int[] v, List snake, int sceneX, int sceneY)
	{
		boolean success = true;
		if (v[0] != 0)
		{
			success = Simulate(GetXDir(v), Math.abs(v[0]), snake, sceneX, sceneY);
		}
		
		if (success)
		{
			if (v[1] != 0)
			{
				success = Simulate(GetYDir(v), Math.abs(v[1]), snake, sceneX, sceneY);
			}
			return success;
		}
		
		return false;
	}
	
	public boolean SimulateYX(int[] v, List snake, int sceneX, int sceneY)
	{
		boolean success = true;
		if (v[1] != 0)
		{
			success = Simulate(GetYDir(v), Math.abs(v[1]), snake, sceneX, sceneY);
		}
		
		if (success)
		{
			if (v[0] != 0)
			{
				success = Simulate(GetXDir(v), Math.abs(v[0]), snake, sceneX, sceneY);
			}
			return success;
		}
		
		return false;
	}
	
	public int ComputeDistance(int[] p1, int[] p2)
	{
		return Math.abs(p1[0] - p2[0]) + Math.abs(p1[1] - p2[1]);
	}
	
	public int[] ChooseClosestFood(List<int[]> foods, int[] snakeHead)
	{
		int[] result = null;
		int min = 10000000;
		for (int[] food : foods)
		{
			if (ComputeDistance(food, snakeHead) < min)
			{
				min = ComputeDistance(food, snakeHead);
				result = food;
			}
		}
		
		return result;
	}
	
	public int[] GetVector(int[] p1, int[] p2)
	{
		int[] v = new int[2];
		v[0] = p2[0] - p1[0];
		v[1] = p2[1] - p1[1];
		return v;
	}

	
	
	public boolean SimulateByOneAndCheckCollision(List<int[]> snake, int sceneX, int sceneY, int[] direction)
	{
		List<int[]> copy = new LinkedList(snake);
		SimulateByOne(direction, copy);
		return IsHeadColliding(sceneX, sceneY, copy);
	}
	
	public int[] RunAlgorithm(List<int[]> food_, List<int[]> snake, int[] sceneDim) throws Exception
	{
		List food = new LinkedList(food_);
		while (true)
		{
			int[] closestPoint = ChooseClosestFood(food, snake.get(0));
			if (closestPoint == null)
			{
				if (!SimulateByOneAndCheckCollision(snake, sceneDim[0], sceneDim[1], LEFT))
					return LEFT;
				if (!SimulateByOneAndCheckCollision(snake, sceneDim[0], sceneDim[1], RIGHT))
					return RIGHT;
				if (!SimulateByOneAndCheckCollision(snake, sceneDim[0], sceneDim[1], UP))
					return UP;
				if (!SimulateByOneAndCheckCollision(snake, sceneDim[0], sceneDim[1], DOWN))
					return DOWN;
				
				// snake cannot turn in any direction throw an exception
				throw new Exception("snake cannot turn in any direction");
			}
			
			int[] vec = GetVector(snake.get(0), closestPoint);
			
			if (vec[0] == 0 && vec[1] == 0)
				throw new Exception("snake head is on food");
				
			boolean success = SimulateXY(vec, new LinkedList(snake), sceneDim[0], sceneDim[1]);
			if (success)
			{
				if (vec[0] != 0)
				{
					return GetXDir(vec);
				}
				else
				{
					return GetYDir(vec);
				}
			}
				
			success = SimulateYX(vec, new LinkedList(snake), sceneDim[0], sceneDim[1]);
			if (success)
			{
				if (vec[1] != 0)
				{
					GetYDir(vec);
				}
				else
				{
					GetXDir(vec);
				}
			}
			
			food.remove(closestPoint);
		}		
	}
		
	public boolean IsFoodOnSnake(List<int[]> snake, List<int[]> food)
	{
		for (int[] sElem : snake)
		{
			for (int[] fElem: food)
			{
				if (sElem[0] == fElem[0] && sElem[1] == fElem[1])
					return true;
			}
		}
		return false;
	}
		
	public int[] IsFoodOnHead(int[] head, List<int[]> food)
	{
		for (int[] fElem: food)
		{
			if (head[0] == fElem[0] && head[1] == fElem[1])
				return fElem;
		}		
		
		return null;
	}
		

		
	public void PrintList(List<int[]> list)
	{
		for (int[] sElem : list)
		{
			System.out.print(" [" + sElem[0] + " " + sElem[1] + "]");
		}	
		System.out.println();
	}
	
	public void PrintInt2(int[] i)
	{
		System.out.println(" [" + i[0] + " " + i[1] + "]");
	}
	
	public void DrawStage(List<int[]> snake, List<int[]> food, int sceneX, int sceneY)
	{
		char[][] toDraw = new char[sceneX][sceneY];
		
		for (int ii = 0; ii < sceneX; ++ii)
		{
			for (int jj = 0; jj < sceneY; ++jj)
			{
				toDraw[ii][jj] = '.';
			}
		}
		
		for (int jj = 0; jj < food.size(); ++jj)
		{
			toDraw[food.get(jj)[0]][food.get(jj)[1]] = '|';
		}		
		
		toDraw[snake.get(0)[0]][snake.get(0)[1]] = '@';
		for (int ii = 1; ii < snake.size(); ++ii)
		{
			toDraw[snake.get(ii)[0]][snake.get(ii)[1]] = 's';
		}
		
		for (int ii = 0; ii < sceneY; ++ii)
		{
			for (int jj = 0; jj < sceneX; ++jj)
			{
				System.out.print(toDraw[jj][ii]);
			}
			System.out.println("");
		}
	}
	
	public void PlayGame()
	{
		while (true)
		{
			// 1. Get necessary info from serwer
			int[][] food = null;
			int[][] snake = null;
			int[] 	sceneDim = null;
			try 
			{
				//food = GetFood();
				snake = GetSnake();	
				sceneDim = GetSceneDim();
			}
			catch (Exception e)
			{
				break;
			}
			
			List<int[]> foodAsList = Arrays.asList(food);
			List<int[]> snakeAsList = Arrays.asList(snake);

			// 2. Run algorithm for this info
			int[] snakeDirection = null;
			try 
			{
				snakeDirection = RunAlgorithm(foodAsList, snakeAsList, sceneDim);
			}
			catch (Exception e)
			{
				break;
			}
			
			// 3. Make call to server based on snakeDirection
			
		}
	}
	
	public static void main(String[] args)
	{
		// here looup rmi registry name
	}


}
